using System;
using Slaks.DocumentSerialization.Document;
using Slaks.Graphs.Comparators;
using Slaks.Graphs.GraphBuilder.Functors;

namespace Slaks.Graphs.GraphBuilder.Slaks.ExperimentalRepresentations
{
	public class SlaksExpereimentalRepresentationBuilder : GraphBuilderBase
	{
		/// <summary>
		/// Removes the rest of infrequent vertices leaving at most (numberOfVerticesToLeave)
		/// Used for temporal graph for quering about existing vertices
		/// </summary>
		/// <param name="numberOfVerticesToLeave"></param>
		public void RemoveInfrequentVerticesInTemporalGraph(Graph temporalGraph, int numberOfVerticesToLeave)
		{
			VertexList vertexList = temporalGraph.GetVertexList();
			Id2VertexMap id2VertexMapper = temporalGraph.GetId2VertexMap();
			if (numberOfVerticesToLeave <= 0) return;
			int totalCount = vertexList.Count;
			if (numberOfVerticesToLeave >= totalCount) return;
			//what is desired length of the vertex list

			temporalGraph.SortVertices(new FrequencyComparer());

			int currentIndex = numberOfVerticesToLeave;

			long idToRemove;
			while (vertexList.Count > numberOfVerticesToLeave)
			{
				idToRemove = vertexList[currentIndex].GetId();
				vertexList.RemoveAt(currentIndex);
				id2VertexMapper.Remove(idToRemove);
			}

		}

		/// <summary>
		/// Builds the temporal graph only with vertices
		/// </summary>
		/// <param name="document"></param>
		/// <returns></returns>
		protected Graph BuildTemporalGraph(Document document, BuilderType builderType)
		{
			Vertex vertex;
			Graph tempGraph = new Graph();

			double frequentNode = double.MinValue;
			double tempFrequentNode;

			#region find frequent node if NormalizedFrequency
			if (builderType == BuilderType.NormalizedFrequencySentenceWeightRepresentationSlaks)
			{
				//find frequent node
				//long mostFrequentId = document.MostFrequencyInfo.TotalMostFrequentId;
				//frequentNode = document.GetNodeById(mostFrequentId).TotalTermFrequency;

				foreach (Node node in document.Nodes)
				{
					tempFrequentNode = node.TotalTermFrequency * 
						(new SentenceWeightFunctor(node.GetMinSentenceNumber())).GetNodeWeight();
					if (frequentNode < tempFrequentNode) frequentNode = tempFrequentNode;
				}
			}
			#endregion
			
			//create temporal graph
			//iterate through nodes
			foreach (Node node in document.Nodes)
			{
				vertex = tempGraph.CreateAddVertex(node.Id);
				if (builderType == BuilderType.StandardSentenceWeightRepresentationSlaks) 
				{
					long minSentenceNumber = node.GetMinSentenceNumber();
					vertex.Frequency = node.TotalTermFrequency *(new SentenceWeightFunctor(minSentenceNumber)).GetNodeWeight();
				}
				else if (builderType == BuilderType.NormalizedFrequencySentenceWeightRepresentationSlaks)
				{
					long minSentenceNumber = node.GetMinSentenceNumber();
					vertex.Frequency = node.TotalTermFrequency *
						(new SentenceWeightFunctor(minSentenceNumber)).GetNodeWeight() / frequentNode ;
				}
				else throw new Exception(builderType + " representation is not supported by SlaksExperimentalRepresentationBuilder");
			}

			if (builderType == BuilderType.NormalizedFrequencySentenceWeightRepresentationSlaks)
			{
				#region set normalized frequency to the edge

				VertexList vertexList = tempGraph.GetVertexList();
				double mostFrequentEdge = long.MinValue;
				double tempEdgeFrequency = 0;
				foreach (Vertex ver in vertexList)
				{
					tempEdgeFrequency = 0;
					foreach (EdgeList edgeLst in ver.GetSection2EdgeMap().Values)
					{
						foreach (Edge edge in edgeLst)
						{
							tempEdgeFrequency += edge.GetFrequency();
						}
					}

					if (mostFrequentEdge < tempEdgeFrequency) mostFrequentEdge = tempEdgeFrequency;
				}

				//set the normalized frequency
				foreach (Vertex ver in vertexList)
				{
					foreach (EdgeList edgeLst in ver.GetSection2EdgeMap().Values)
					{
						foreach (Edge edge in edgeLst)
						{
							edge.SetFrequency(edge.GetFrequency()/mostFrequentEdge);
						}
					}
				}

				#endregion
			}

			return tempGraph;
		}

		/// <summary>
		/// Add edges between nodes
		/// </summary>
		/// <param name="document"></param>
		/// <param name="graph">graph to build</param>
		/// <param name="temporalGraph">temporal graph which hold only frequent nodes. it will be queried for them</param>
		/// <param name="currentVertex">the current node</param>
		/// <param name="nParameter">the n-distance parameter</param>
		/// <param name="nDistanceCount">current distance counter</param>
		/// <param name="sentence">current sentence</param>
		/// <param name="sectionType">section type</param>
		protected void AddRelations(Document document,
		                            Graph graph,
		                            Graph temporalGraph,
		                            Vertex currentVertex,
		                            int nParameter,
		                            int nDistanceCount,
		                            Sentence sentence,
		                            Section.SectionType sectionType,
		                            BuilderType builderType
			)
		{
			if (nDistanceCount > nParameter) return;
			Node node;
			long nextId;
			Vertex nextIdVertex;
			if (sentence.NextId >= 0)
			{
				//get the nextId.
				nextId = sentence.NextId;
				//we must check whether the nextId is in the list of frequent nodes.
				//if not then we must skip it.

				nextIdVertex = temporalGraph.GetVertex(nextId);

				node = document.GetNodeById(nextId);
				Section section = node.GetSectionByType(sectionType);
				if (section == null) return;
				Sentence nextSentence = section.GetSentence(sentence.Number, sentence.Place + 1);
				if (nextSentence == null) return;

				string sectionLabel;

				if (nextIdVertex != null) //the nextIdVertex is among frequent nodes
				{
					if (builderType == BuilderType.StandardSentenceWeightRepresentationSlaks) sectionLabel = section.GetSectionTypeString();
					else if (builderType == BuilderType.NormalizedFrequencySentenceWeightRepresentationSlaks) sectionLabel = "norm";
					else throw new GraphException("AddRelations() : BuilderType is not supported for this kind of relation " + builderType.ToString());

					nextIdVertex = graph.CreateAddVertex(nextId);
					currentVertex.AddEdge(nextIdVertex, sectionLabel);

					AddRelations(document, graph, temporalGraph, currentVertex, nParameter, nDistanceCount + 1, nextSentence, sectionType, builderType);
				}
				else
				{
					//the nextIdVertex is not frequent, then we must skip it and find the frequent node.
					AddRelations(document, graph, temporalGraph, currentVertex, nParameter, nDistanceCount, nextSentence, sectionType, builderType);
				}
			}
			return;
		}

	}
}